package socket

import (
	"encoding/json"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"sync"
	"toyota-server/init/global"
	j_log "toyota-server/init/j-log"
	"toyota-server/utils/tools"
)

type WSClient struct {
	Conn         *websocket.Conn `json:"-"`
	Uid          int             `json:"uid"`
	CreateTime   string          `json:"create_time"`
	LastDataTime string          `json:"last_data_time"`
	RemoteAddr   string          `json:"remote_addr"`
	WriteLock    sync.Mutex      `json:"-"`
	CloseSignal  chan uint8      `json:"-"`
}

type WSResponseMessage struct {
	Code int    `json:"code"` // 0.操作成功 1.操作失败 2.推送变量 3. 推送报警
	Msg  string `json:"msg"`
	Data string `json:"data"`
}

var WSClientMap *WSClientMapStu
var wsRegister chan *WSClient   // client 注册管道
var wsUnregister chan *WSClient // client 退出管道

func WSRun() {
	WSClientMap = NewWSClientMap()
	wsRegister = make(chan *WSClient, 10000)
	wsUnregister = make(chan *WSClient, 10000)

	addr := global.AppConfig.Section("websocket").Key("addr").String()
	log.SetFlags(0)
	go wsConnManage()
	http.HandleFunc("/echo", echo)
	j_log.LogInfo([]interface{}{"websocket listen on " + addr})
	log.Fatal(http.ListenAndServe(addr, nil))
}

var httpUpGrader = websocket.Upgrader{
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

func echo(w http.ResponseWriter, r *http.Request) {
	conn, err0 := httpUpGrader.Upgrade(w, r, nil)
	if err0 != nil {
		log.Print("upgrade:", err0)
		return
	}
	client := NewWSClient(conn)
	wsRegister <- client
	go client.handleWSConnect()
}

func (client *WSClient) handleWSConnect() {
	defer func() {
		_ = client.Conn.Close()
		wsUnregister <- client
	}()
	for {
		_, message, err := client.Conn.ReadMessage()
		if err != nil {
			if websocket.IsCloseError(err, websocket.CloseNormalClosure, websocket.CloseGoingAway) {
				j_log.LogInfo([]interface{}{client.Conn.RemoteAddr().String(), "用户离线", err.Error()})
			} else {
				j_log.LogError([]interface{}{"conn.ReadMessage error", err.Error()})
			}
			return
		}
		client.LastDataTime = tools.GetNow()
		req := WSResponseMessage{}
		err = json.Unmarshal(message, &req)
		if err != nil {
			_ = client.SendMessage(1, "请求参数解析失败 "+err.Error(), string(message))
			continue
		}
		err = WSClientMap.BroadcastMessage(req.Code, req.Msg, req.Data)
		if err != nil {
			j_log.LogError([]interface{}{"BroadcastMessage ERROR", err.Error()})
		}
	}
}

// 连接池管理, 检查连接是否在线, 删除离线的client
func wsConnManage() {
	for {
		select {
		case client := <-wsRegister:
			existCli, ok := WSClientMap.Get(client.RemoteAddr)
			if ok {
				j_log.LogInfo([]interface{}{client.Conn.RemoteAddr().String(), "WS客户端复用旧连接"})
				existCli.WriteLock.Lock()
				existCli.Conn = client.Conn
				existCli.LastDataTime = tools.GetNow()
				existCli.WriteLock.Unlock()
			} else {
				WSClientMap.Set(client.RemoteAddr, client)
				j_log.LogInfo([]interface{}{client.Conn.RemoteAddr().String(), "WS新客户端已连接"})
			}
		case client := <-wsUnregister:
			client.CloseSignal <- 1
			WSClientMap.Delete(client.RemoteAddr)
		}
	}
}

func (client *WSClient) SendMessage(code int, msg string, data string) (err error) {
	resp := WSResponseMessage{}
	resp.Code = code
	resp.Msg = msg
	resp.Data = data
	jsonB, err := json.Marshal(&resp)
	if err != nil {
		j_log.LogError([]interface{}{"SendMessage json.Marshal ERROR", err.Error()})
		return
	}
	client.WriteLock.Lock()
	defer client.WriteLock.Unlock()
	err = client.Conn.WriteMessage(1, jsonB)
	if err != nil {
		j_log.LogError([]interface{}{"SendMessage WriteMessage ERROR", err.Error()})
		return
	}
	return
}

/*--- WSClientMapStu ---*/

type WSClientMapStu struct {
	M map[string]*WSClient
	sync.RWMutex
}

func (ws *WSClientMapStu) Set(key string, v *WSClient) {
	ws.Lock()
	defer ws.Unlock()
	ws.M[key] = v
	return
}

func (ws *WSClientMapStu) Get(key string) (v *WSClient, ok bool) {
	ws.RLock()
	defer ws.RUnlock()
	v, ok = ws.M[key]
	return
}

func (ws *WSClientMapStu) Delete(key string) {
	ws.Lock()
	defer ws.Unlock()
	delete(ws.M, key)
}

func (ws *WSClientMapStu) BroadcastMessage(code int, msg string, data string) (err error) {
	resp := WSResponseMessage{}
	resp.Code = code
	resp.Msg = msg
	resp.Data = data
	jsonB, err2 := json.Marshal(&resp)
	if err2 != nil {
		j_log.LogError([]interface{}{"SendMessage json.Marshal ERROR", err2.Error()})
		return err2
	}
	ws.RLock()
	defer ws.RUnlock()
	for _, client := range ws.M {
		client.WriteLock.Lock()
		err = client.Conn.WriteMessage(1, jsonB)
		client.WriteLock.Unlock()
		if err != nil {
			j_log.LogError([]interface{}{"SendMessage WriteMessage ERROR", err.Error()})
			return
		}
	}
	return
}

func NewWSClientMap() *WSClientMapStu {
	ws := &WSClientMapStu{}
	ws.M = make(map[string]*WSClient)
	return ws
}

func NewWSClient(conn *websocket.Conn) *WSClient {
	client := WSClient{
		Conn:         conn,
		Uid:          0,
		CreateTime:   tools.GetNow(),
		LastDataTime: tools.GetNow(),
		RemoteAddr:   conn.RemoteAddr().String(),
	}
	client.CloseSignal = make(chan uint8, 100)
	return &client
}
